using System.Linq;
using System.Reflection;
using UnityEngine;
using System.Collections.Generic;

namespace cframework_xlua
{
    [System.AttributeUsage(System.AttributeTargets.All, AllowMultiple = true, Inherited = true)]
    public class EssentialToolsAttribute : System.Attribute
    {
        public string[] toolNames;
        public string downloadUrl;
        public EssentialToolsAttribute(string[] _toolNames, string downloadUrl = null)
        {
            toolNames = _toolNames;
            this.downloadUrl = downloadUrl;
        }

        public EssentialToolsAttribute(string _toolName, string downloadUrl = null)
        {
            toolNames = new string[] { _toolName };
            this.downloadUrl = downloadUrl;
        }


        public static (string, string, string) Check(ScriptableObject o, DeviceToolPathSetting toolPathsSetting)
        {
            var attrs = o.GetType().GetCustomAttributes<EssentialToolsAttribute>(true);
            foreach (var f in attrs)
            {
                foreach (var t in f.toolNames)
                {
                    var toolPath = toolPathsSetting.GetCmdToolPath(t);
                    if (string.IsNullOrEmpty(toolPath))
                    {
                        return (t, null, f.downloadUrl);
                    }
                    else if (!System.IO.File.Exists(toolPath))
                    {
                        return (t, toolPath, null);
                    }
                }
            }

            var fieldInfos = o.GetType().GetFields(BindingFlags.Public | BindingFlags.Instance | BindingFlags.FlattenHierarchy);
            foreach (var f in fieldInfos)
            {
                if (f.FieldType == typeof(ScriptableObject) || f.FieldType.IsSubclassOf(typeof(ScriptableObject)))
                {
                    var v = f.GetValue(o);
                    var ret = Check(v as ScriptableObject, toolPathsSetting);
                    if (ret.Item1 != null)
                    {
                        return ret;
                    }
                }
            }

            return (null, null, null);
        }

        public static void GetAllToolPaths(ScriptableObject o, DeviceToolPathSetting toolPathsSetting, string fieldPath, List<(string, string, string)> results)
        {
            if(o == null)
            {
                return;
            }

            if(fieldPath == null)
            {
                fieldPath = o.name;
            }

            var attrs = o.GetType().GetCustomAttributes<EssentialToolsAttribute>(true);
            
            foreach (var f in attrs)
            {
                foreach (var t in f.toolNames)
                {
                    var toolPath = toolPathsSetting.GetCmdToolPath(t);
                    if (!string.IsNullOrEmpty(toolPath) && System.IO.File.Exists(toolPath))
                    {
                        results.Add((t, toolPath, fieldPath));
                    }
                    else
                    {
                        results.Add((t, f.downloadUrl, fieldPath));
                    }
                }
            }

            var fieldInfos = o.GetType().GetFields(BindingFlags.Public | BindingFlags.Instance | BindingFlags.FlattenHierarchy);
            foreach (var f in fieldInfos)
            {
                if (f.FieldType == typeof(ScriptableObject) || f.FieldType.IsSubclassOf(typeof(ScriptableObject)))
                {
                    var v = f.GetValue(o);

                    GetAllToolPaths(v as ScriptableObject, toolPathsSetting, fieldPath + "." + f.Name, results);
                }
            }
        }
    }


    [System.AttributeUsage(System.AttributeTargets.Field | System.AttributeTargets.Property, AllowMultiple = false, Inherited = false)]
    public class EssentialAttribute : System.Attribute
    {
        object invalidValue;
        string link;

        public EssentialAttribute(object _invalidValue = null, string link = null)
        {
            invalidValue = _invalidValue;
        }

        public static List<string> Check(object o, string parentFields = null, System.Collections.Generic.List<string> needFields = null)
        {
            if (o == null)
            {
                return null;
            }

            if (needFields == null)
            {
                needFields = new List<string>();
            }

            var fieldInfos = o.GetType().GetFields(BindingFlags.Public | BindingFlags.Instance | BindingFlags.FlattenHierarchy);

            foreach (var f in fieldInfos)
            {
                var attr = f.GetCustomAttribute<EssentialAttribute>();
                if (attr != null)
                {
                    if (f.FieldType == typeof(string))
                    {
                        if (string.IsNullOrEmpty((string)f.GetValue(o)))
                        {
                            needFields.Add(parentFields + "." + f.Name);
                        }
                    }
                    else if (f.FieldType == typeof(int))
                    {
                        if (0 == (int)f.GetValue(o))
                        {
                            needFields.Add(parentFields + "." + f.Name);
                        }
                    }
                    else if (!f.FieldType.IsValueType)
                    {
                        if (f.GetValue(o) == attr.invalidValue)
                        {
                            needFields.Add(parentFields + "." + f.Name);
                        }
                    }
                    else if (f.FieldType.IsEnum)
                    {
                        if (f.GetValue(o) == attr.invalidValue)
                        {
                            needFields.Add(parentFields + "." + f.Name);
                        }
                    }
                    else
                    {
                        throw new System.Exception("");
                    }
                }

                if (f.FieldType == typeof(object) || f.FieldType.IsSubclassOf(typeof(object)))
                {
                    var oo = f.GetValue(o);
                    Check(oo, parentFields + "." + f.Name, needFields);
                }
            }

            return needFields;
        }
    }
}